package com.david.mydemo.ctl_redis.controller;

import com.alibaba.fastjson.JSON;
import com.david.mydemo.ctl_redis.dto.UserDto;
import com.david.mydemo.framework.annotation.MySysLog;
import com.david.mydemo.util.MyConstants;
import com.david.mydemo.util.MyResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@RestController
@RequestMapping(value = "/redis")
@Api(tags = "Redis Controller")
public class RedisController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //获取线程前缀，同时也是线程表示。通过UUID唯一性
    private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";

    @RequestMapping(value = "/tryLock", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "SETNX分布式锁-获取锁")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "SETNX分布式锁-获取锁", module = MyConstants.MODULE_COMMON)
    public boolean tryLock() {
        String key = "orderId";
        long timeOut = 600;
        //获取线程id
        String id = ID_PREFIX + Thread.currentThread().getId();
        //获取锁
        Boolean absent = stringRedisTemplate.opsForValue().setIfAbsent(key, id, timeOut, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(absent);
    }

    @RequestMapping(value = "/unLock", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "SETNX分布式锁-释放锁")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "SETNX分布式锁-释放锁", module = MyConstants.MODULE_COMMON)
    public void unLock() {
        String key = "orderId";
        //获取存储的线程标识
        String value = stringRedisTemplate.opsForValue().get(key);
        //当前线程的线程标识
        String id = ID_PREFIX + Thread.currentThread().getId();
        //线程标识相同则删除否,则不删除
        if (id.equals(value)) {
            stringRedisTemplate.delete(key);
        }
    }

    @RequestMapping(value = "/setRedisString", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "字符串存储")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "字符串存储", module = MyConstants.MODULE_COMMON)
    public MyResponse setRedisString() {
        // 存取string
        String key = "userName";
        stringRedisTemplate.opsForValue().set(key, "张三");
        String result = stringRedisTemplate.opsForValue().get(key);
        log.info("获取字符串结果：" + result);

        // 存取对象，转json
        String key1 = "user";
        UserDto userDto = new UserDto();
        userDto.setCode("zhangsan");
        userDto.setName("张三");
        stringRedisTemplate.opsForValue().set(key1, JSON.toJSONString(userDto));
        String result1 = stringRedisTemplate.opsForValue().get(key1);
        UserDto userDto1 = JSON.parseObject(result1, UserDto.class);
        log.info("获取字符串结果，code: {}，userName: {}", userDto1.getCode(), userDto1.getName());

        return new MyResponse().success();
    }

    @RequestMapping(value = "/setRedisHash", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "哈希存储")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "哈希存储", module = MyConstants.MODULE_COMMON)
    public MyResponse setRedisHash() {
        // 通过redis操作模板获取操作类型对象
        // 这段注释描述了使用StringRedisTemplate的opsForHash()方法获取操作类型对象的过程
        HashOperations<String, String, String> opsForHash = stringRedisTemplate.opsForHash();

        // 添加一个键值对到哈希表中, 参数说明：, - key: 哈希表的键, - hashKey: 哈希表中的键, - value: 哈希表中的值
        // 返回值：如果哈希表中不存在给定的键，添加成功并返回true；如果哈希表中已经存在给定的键，更新值并返回false
        opsForHash.put("hash:key", "hashField", "hashValue");

        // 批量添加多个键值对到哈希表中, 参数说明：, - key: 哈希表的键, - m: 要添加的多个键值对, 返回值：返回成功添加的键值对数量
//        opsForHash.putAll("hash:key", Map.of("field1", "value1", "field2", "value2"));
        Map map = new HashMap();
        map.put("field1", "value1");
        opsForHash.putAll("hash:key", map);

        // 获取存储在哈希表中指定字段的值, 参数说明：, - key: 哈希表的键, - hashKey: 哈希表中的键, 返回值：返回哈希表中指定字段的值
        String getResult = opsForHash.get("hash:key", "hashField");

        // 删除哈希表中的一个或多个字段, 参数说明：, - key: 哈希表的键, - hashKeys: 要删除的字段, 返回值：返回成功删除的字段数量
        Long deleteResult = opsForHash.delete("hash:key", "hashField1", "hashField2");

        // 判断哈希表中是否存在指定字段, 参数说明：, - key: 哈希表的键, - hashKey: 要检查的字段
        // 返回值：如果哈希表中存在给定的字段，返回true；如果哈希表中不存在给定的字段或key不存在，返回false
        Boolean hasKeyResult = opsForHash.hasKey("hash:key", "hashField");

        // 获取哈希表中所有的字段-值对, 参数说明： - key: 哈希表的键, 返回值：返回哈希表中所有的字段-值对
        Map entriesResult = opsForHash.entries("hash:key");

        // 获取哈希表中所有的字段, 参数说明： - key: 哈希表的键, 返回值：返回哈希表中所有的字段
        Set<String> keysResult = opsForHash.keys("hash:key");

        // 获取哈希表中所有的值, 参数说明：- key: 哈希表的键, 返回值：返回哈希表中所有的值
        List<String> valuesResult = opsForHash.values("hash:key");

        // 获取哈希表中字段的数量,参数说明：- key: 哈希表的键, 返回值：返回哈希表中字段的数量
        Long sizeResult = opsForHash.size("hash:key");

        //将指定位置的元素替换为新值, 其中"myList"是待操作的列表键, index是要替换的位置, value是新的值
//        opsForHash.set("myList", index, value);

        return new MyResponse().success();
    }

    @RequestMapping(value = "/setRedisList", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "List集合存储")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "List集合存储", module = MyConstants.MODULE_COMMON)
    public MyResponse setRedisList() {
        // 通过redis操作模板获取操作类型对象
        // 这段注释描述了使用StringRedisTemplate的opsForList()方法获取操作类型对象的过程
        ListOperations<String, String> opsForList = stringRedisTemplate.opsForList();

        // 将一个或多个值插入到列表头部, 参数说明：, - key: 要操作的列表键, - values: 要插入的值, 返回值：返回列表的长度
//        Long leftPushResult = opsForList.leftPush("list:key", "value1", "value2", "value3");
        Long leftPushResult = opsForList.leftPush("list:key", "value1");

        // 将一个或多个值插入到列表尾部, 参数说明：, - key: 要操作的列表键, - values: 要插入的值, 返回值：返回列表的长度
//        Long rightPushResult = opsForList.rightPush("list:key", "value1", "value2", "value3");
        Long rightPushResult = opsForList.rightPush("list:key", "value1");

        // 获取列表指定范围内的元素, 参数说明：, - key: 要操作的列表键, - start: 起始位置（0表示第一个元素）, - end: 结束位置（-1表示最后一个元素）
        // 返回值：返回指定范围内的元素列表
        List<String> rangeResult = opsForList.range("list:key", 0, -1);

        // 移除列表中与给定值相等的元素
        // 参数说明：, - key: 要操作的列表键, - count: 移除元素的个数（正数表示从左到右移除，负数表示从右到左移除，0表示移除所有与值相等的元素）, - value: 要移除的值
        // 返回值：返回被移除元素的个数
        Long removeResult = opsForList.remove("list:key", 0, "value");

        // 获取列表中指定位置的元素, 参数说明：, - key: 要操作的列表键, - index: 要获取元素的索引位置（0表示第一个元素，-1表示最后一个元素）
        // 返回值：返回指定位置的元素
        String indexResult = opsForList.index("list:key", 0);

        // 获取列表的长度, 参数说明：, - key: 要操作的列表键, 返回值：返回列表的长度
        Long sizeResult = opsForList.size("list:key");

        // 从列表的左侧弹出一个元素, 参数说明：, - key: 要操作的列表键, 返回值：返回被弹出的元素
        String leftPopResult = opsForList.leftPop("list:key");

        // 从列表的右侧弹出一个元素, 参数说明：, - key: 要操作的列表键, 返回值：返回被弹出的元素
        String rightPopResult = opsForList.rightPop("list:key");

        // 从一个列表的右侧弹出一个元素，并将它推到另一个列表的左侧
        // 参数说明：, - sourceKey: 要弹出元素的列表键, - destinationKey: 要推入元素的列表键, 返回值：返回被弹出的元素
        String rightPopAndLeftPushResult = opsForList.rightPopAndLeftPush("source:key", "destination:key");

        // 裁剪列表，保留指定范围内的元素, 参数说明：, - key: 要操作的列表键, - start: 起始位置（0表示第一个元素）, - end: 结束位置（-1表示最后一个元素）, 返回值：无
        opsForList.trim("list:key", 0, 9);

        return new MyResponse().success();
    }

    @RequestMapping(value = "/setRedisSet", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "Set集合存储")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "Set集合存储", module = MyConstants.MODULE_COMMON)
    public MyResponse setRedisSet() {
        // 通过redis操作模板获取操作类型对象
        // 这段注释描述了使用StringRedisTemplate的opsForSet()方法获取操作类型对象的过程
        SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();

        // 向集合添加一个或多个成员, 参数说明：, - key: 要操作的集合键, - values: 要添加的成员, 返回值：返回成功添加的成员数量
        Long addResult = opsForSet.add("set:key", "member1", "member2", "member3");

        // 获取集合的成员数, 参数说明：, - key: 要操作的集合键, 返回值：返回集合的成员数
        Long sizeResult = opsForSet.size("set:key");

        // 判断集合中是否包含指定成员, 参数说明：, - key: 要操作的集合键, - value: 要检查的成员, 返回值：如果成员存在于集合中，返回true；否则返回false
        Boolean isMemberResult = opsForSet.isMember("set:key", "member");

        // 获取集合中的所有成员, 参数说明：, - key: 要操作的集合键, 返回值：返回集合中的所有成员
        Set<String> membersResult = opsForSet.members("set:key");

        // 从集合中随机弹出一个成员, 参数说明：, - key: 要操作的集合键, 返回值：返回被弹出的成员
        String popResult = opsForSet.pop("set:key");

        // 将指定成员从一个集合移动到另一个集合, 参数说明：, - key: 源集合键, - value: 要移动的成员, - destKey: 目标集合键
        // 返回值：如果成员成功移动，返回true；如果成员不存在于源集合，返回false
        Boolean moveResult = opsForSet.move("source:set:key", "member", "destination:set:key");

        // 从集合中移除一个或多个成员, 参数说明：, - key: 要操作的集合键, - values: 要移除的成员, 返回值：返回成功移除的成员数量
        Long removeResult = opsForSet.remove("set:key", "member1", "member2");

        // 获取两个集合的交集, 参数说明：, - key: 要操作的集合键, - otherKeys: 其他集合的键, 返回值：返回交集的成员集合
        Set<String> intersectResult = opsForSet.intersect("set:key", "other:set:key");

        // 获取两个集合的并集, 参数说明：, - key: 要操作的集合键, - otherKeys: 其他集合的键, 返回值：返回并集的成员集合
        Set<String> unionResult = opsForSet.union("set:key", "other:set:key");

        // 获取两个集合的差集, 参数说明：, - key: 要操作的集合键, - otherKeys: 其他集合的键, 返回值：返回差集的成员集合
        Set<String> differenceResult = opsForSet.difference("set:key", "other:set:key");

        return new MyResponse().success();
    }

    @RequestMapping(value = "/setRedisSortedSet", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "Sorted Set集合存储")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "Sorted Set集合存储", module = MyConstants.MODULE_COMMON)
    public MyResponse setRedisSortedSet() {
        // 通过redis操作模板获取操作类型对象
        // 这段注释描述了使用StringRedisTemplate的opsForZSet()方法获取操作类型对象的过程
        ZSetOperations<String, String> opsForZSet = stringRedisTemplate.opsForZSet();

        // 向有序集合添加一个或多个成员，或者更新已存在成员的分数, 参数说明：, - key: 要操作的有序集合键, - values: 要添加或更新的成员及其分数
        // 返回值：返回成功添加或更新的成员数量
        Boolean addResult = opsForZSet.add("zset:key", "member1", 1.0);
        //批量添加
        Set<ZSetOperations.TypedTuple<String>> memberScores = new HashSet<>();
        memberScores.add(new DefaultTypedTuple("member2", 30.0));
        memberScores.add(new DefaultTypedTuple("member3", 20.0));
        Long multiAddResult = opsForZSet.add("zset:key", memberScores);

        // 增加指定成员的分数, 参数说明：, - key: 要操作的有序集合键, - member: 要增加分数的成员, - delta: 分数增量, 返回值：返回成员新的分数值
        Double incrementScoreResult = opsForZSet.incrementScore("zset:key", "member", 2.5);

        // 获取有序集合的成员数, 参数说明：, - key: 要操作的有序集合键, 返回值：返回有序集合的成员数
        Long sizeResult = opsForZSet.size("zset:key");

        // 获取有序集合中指定成员的分数, 参数说明：, - key: 要操作的有序集合键, - member: 要获取分数的成员, 返回值：返回成员的分数
        Double scoreResult = opsForZSet.score("zset:key", "member");

        // 获取有序集合中指定范围内的成员及其分数，按分数从低到高排序, 参数说明：, - key: 要操作的有序集合键, - start: 起始位置（0表示第一个成员）, - end: 结束位置（-1表示最后一个成员）
        // 返回值：返回指定范围内的成员及其分数的集合
        Set<ZSetOperations.TypedTuple<String>> rangeWithScoresResult = opsForZSet.rangeWithScores("zset:key", 0, -1);

        // 获取有序集合中指定范围内的成员及其分数，按分数从高到低排序, 参数说明：, - key: 要操作的有序集合键, - start: 起始位置（0表示第一个成员）, - end: 结束位置（-1表示最后一个成员）
        // 返回值：返回指定范围内的成员及其分数的集合
        Set<ZSetOperations.TypedTuple<String>> reverseRangeWithScoresResult = opsForZSet.reverseRangeWithScores("zset:key", 0, -1);

        // 获取有序集合中指定范围内的成员数量，分数介于min和max之间（包含min和max）, 参数说明：, - key: 要操作的有序集合键, - min: 最小分数, - max: 最大分数
        // 返回值：返回指定范围内的成员数量
        int min = 1;
        int max = 10;
        Long countResult = opsForZSet.count("zset:key", min, max);

        // 从有序集合中移除指定范围内的成员，分数介于min和max之间（包含min和max）, 参数说明：, - key: 要操作的有序集合键, - min: 最小分数, - max: 最大分数
        // 返回值：返回成功移除的成员数量
        Long removeRangeByScoreResult = opsForZSet.removeRangeByScore("zset:key", min, max);

        // 根据排名范围移除有序集合中的成员（排名从0开始）, 参数说明：, - key: 要操作的有序集合键, - start: 起始排名, - end: 结束排名
        // 返回值：返回成功移除的成员数量
        long start = 0;
        long end = 10;
        Long removeRangeResult = opsForZSet.removeRange("zset:key", start, end);

        return new MyResponse().success();
    }

    @RequestMapping(value = "/sendMessage", method = {RequestMethod.GET, RequestMethod.POST})
    @ApiOperation(value = "发送消息")
    @MySysLog(operate = MyConstants.OPERATE_COMMON, content = "发送消息", module = MyConstants.MODULE_COMMON)
    public MyResponse sendMessage() {
        String channel1 = "user-topic1";
        UserDto userDto1 = new UserDto();
        userDto1.setCode("zhangsan1");
        userDto1.setName("张三1");
        stringRedisTemplate.convertAndSend(channel1, JSON.toJSONString(userDto1));

        UserDto userDto2 = new UserDto();
        userDto2.setCode("zhangsan2");
        userDto2.setName("张三2");
        String channel2 = "user-topic2";
        stringRedisTemplate.convertAndSend(channel2, JSON.toJSONString(userDto2));

        return new MyResponse().success();
    }

}
